package com.sun.umbrella.filter;

import com.alibaba.dubbo.rpc.RpcException;
import com.sun.core.enums.BaseErrorEnum;
import com.sun.core.ex.BusinessCheckFailException;
import com.sun.umbrella.remote.RemoteService;
import com.sun.umbrella.remote.entity.FilterChains;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.NamedFilterList;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.springframework.util.CollectionUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author Jay
 * @version v1.0
 * @description 自定义shiro过滤器路径匹配器
 * @date 2017-12-03 20:13
 */

@Slf4j
public class UmbrellaPathMatchingFilterChainResolver extends PathMatchingFilterChainResolver {


    private RemoteService remoteService;

    private Integer sysId;

    private Boolean enableFilter;

    /**
     * 构造器注入
     * 
     * @param sysId
     * @param enableFilter
     * @param remoteService
     */
    public UmbrellaPathMatchingFilterChainResolver(Integer sysId, Boolean enableFilter, RemoteService remoteService) {
        this.sysId = sysId;
        this.enableFilter = enableFilter;
        this.remoteService = remoteService;
    }


    /**
     * 找到请求url对应的过滤器
     *
     * @param request
     * @param response
     * @param originalChain
     * @return
     */
    @Override
    public synchronized FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {
        FilterChainManager filterChainManager = this.getFilterChainManager();
        // 加载过滤器链
        if (remoteService.isNeedReload(sysId)){
            this.getFilterChainDefinitionMap(filterChainManager);
        }
        if (!filterChainManager.hasChains()) {
            return null;
        } else {
            // 获取请求的url
            String requestUrl = this.getPathWithinApplication(request);
            // 迭代器循环找到是否存在匹配的过滤器，如果存在则放入过滤器链中
            Iterator iterator = filterChainManager.getChainNames().iterator();
            String pathPattern;
            do {
                if (!iterator.hasNext()) {
                    return null;
                }
                pathPattern = (String) iterator.next();
            } while (!this.pathMatches(pathPattern, requestUrl));

            if (log.isTraceEnabled()) {
                log.trace("Matched path pattern [" + pathPattern + "] for requestUrl [" + requestUrl + "].  " + "Utilizing corresponding filter chain...");
            }

            return filterChainManager.proxy(originalChain, pathPattern);
        }
    }


    /**
     * 重新加载过滤器
     *
     * @param filterChainManager
     */
    public void getFilterChainDefinitionMap(FilterChainManager filterChainManager) {
        try {
            if (sysId == null || !enableFilter) {
                return;
            }
            // 获取过滤器链
            List<FilterChains> filterChainsList = remoteService.getFilterChainsBySysId(sysId);
            if (CollectionUtils.isEmpty(filterChainsList)) {
                return;
            }
            // 如果filterChainManager内还存在过滤器则清空然后重新注入
            this.clearFilterChains(filterChainManager);

            // 重新加载过滤器
            for (FilterChains filterChainsDO : filterChainsList) {
                filterChainManager.createChain(filterChainsDO.getMatchSign(), filterChainsDO.getFilterRes());
            }
        } catch (RpcException e) {
            log.error("重新加载过滤器失败，失败的原因为:{}", e);
            throw new BusinessCheckFailException(BaseErrorEnum.BNS_CHK_ERROR.getCode(),"重新加载过滤器失败");
        }
    }

    /**
     * 清空filterChainManager内过滤器
     *
     * @param filterChainManager
     */
    private void clearFilterChains(FilterChainManager filterChainManager) {
        if (filterChainManager == null) {
            return;
        }
        Map<String, NamedFilterList> map = ((DefaultFilterChainManager) filterChainManager).getFilterChains();
        if (!CollectionUtils.isEmpty(map)) {
            map.clear();
        }
    }
}